
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using DS.SludgeEngine.Base;
namespace DS.SludgeEngine.Camera
{
    public class CameraManager : GameComponent, ICameraManager, ICameraComponent
    {
        private const float DEFAULT_SPEED_MOUSE_WHEEL = 1.0f;
        private const float DEFAULT_SPEED_ROTATION = 0.3f;
        private RtsCamera camera;
        private float rotationSpeed;
        private float mouseWheelSpeed;
        public bool checkInput = true;

        #region Public Methods
        public CameraManager(Game game)
            : base(game)
        {
                    

            camera = new RtsCamera();
            rotationSpeed = DEFAULT_SPEED_ROTATION;
            mouseWheelSpeed = DEFAULT_SPEED_MOUSE_WHEEL;
            Rectangle clientBounds = game.Window.ClientBounds;
            float aspect = (float)clientBounds.Width / (float)clientBounds.Height;
            Perspective(CameraSetupInfo.FovX, aspect, CameraSetupInfo.ZNear, CameraSetupInfo.ZFar);
            Game.Activated += new EventHandler(HandleGameActivatedEvent);
            Game.Deactivated += new EventHandler(HandleGameDeactivatedEvent);
            UpdateOrder = 1;
        }

        public override void Initialize()
        {
            base.Initialize();
        }

        public void LookAt(Vector3 target)
        {
            camera.LookAt(target);
        }

        public void Move(float dx, float dy, float dz)
        {
            camera.Move(dx, dy, dz);
        }

        public void Perspective(float fovx, float aspect, float znear, float zfar)
        {
            camera.Perspective(fovx, aspect, znear, zfar);
        }

        public Matrix Perspective(float znear, float zfar)
        {
            return camera.Perspective(znear, zfar);
        }

        public void Rotate(float headingDegrees, float pitchDegrees, float rollDegrees)
        {
            camera.Rotate(headingDegrees, pitchDegrees, rollDegrees);
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            Vector3 delta = new Vector3();
            if (checkInput)
            {
                delta = UpdateInput();
            }
            UpdateCamera(gameTime, delta);
        }

        public void Zoom(float zoom, float minZoom, float maxZoom)
        {
            camera.Zoom(zoom, minZoom, maxZoom);
        }

        #endregion

        #region Private Methods

        private void GetMovementDirection(out Vector3 direction)
        {
            direction.X = 0.0f;
            direction.Y = 0.0f;
            direction.Z = 0.0f;

            IInputControllerService inputController = (IInputControllerService)base.Game.Services.GetService(typeof(IInputControllerService));
            if (inputController != null)
            {
                if (inputController.IsKeyDown(Actions.CameraUp))
                {
                    direction.Z += 1.0f;
                }
                if (inputController.IsKeyDown(Actions.CameraDown))
                {
                    direction.Z -= 1.0f;
                }
                if (inputController.IsKeyDown(Actions.CameraLeft))
                {
                    direction.X -= 1.0f;
                }
                if (inputController.IsKeyDown(Actions.CameraRight))
                {
                    direction.X += 1.0f;
                }
            }
        }

        private void HandleGameActivatedEvent(object sender, EventArgs e)
        {
        }

        private void HandleGameDeactivatedEvent(object sender, EventArgs e)
        {
        }

        private void RotateSmoothly(float headingDegrees, float pitchDegrees, float rollDegrees)
        {
            headingDegrees *= rotationSpeed;
            pitchDegrees *= rotationSpeed;
            rollDegrees *= rotationSpeed;

            Rotate(headingDegrees, pitchDegrees, rollDegrees);
        }

        private Vector3 UpdateInput()
        {
            IMouseController mouseController = (IMouseController)base.Game.Services.GetService(typeof(IMouseController));
            Vector3 delta = new Vector3();

            if (mouseController != null && mouseController.CurrentState.RightButton == ButtonState.Pressed)
            {
                delta.X = -(mouseController.PreviousState.X - mouseController.CurrentState.X);
            }
            return delta;
        }

        private void UpdateCamera(GameTime gameTime, Vector3 delta)
        {
            IMouseController mouseController = (IMouseController)base.Game.Services.GetService(typeof(IMouseController));
            Vector3 direction = new Vector3();
            GetMovementDirection(out direction);
            RotateSmoothly(delta.X, delta.Y, 0.0f);
            if (mouseController != null)
            {
                if ((delta.Z = mouseController.MouseWheelStatus * mouseWheelSpeed) != 0.0f)
                {
                    camera.Zoom(delta.Z, camera.OrbitMinZoom, camera.OrbitMaxZoom);
                }
                Move(direction.X, direction.Y, direction.Z);
            }
        }

        #endregion

        #region Properties


        /// <summary>
        /// Property to get and set the speed of the mouse wheel.
        /// This is used to zoom in and out when the camera is orbiting.
        /// </summary>
        public float MouseWheelSpeed
        {
            get { return mouseWheelSpeed; }
            set { mouseWheelSpeed = value; }
        }

        /// <summary>
        /// Property to get and set the max orbit zoom distance.
        /// </summary>
        public float OrbitMaxZoom
        {
            get { return camera.OrbitMaxZoom; }
            set { camera.OrbitMaxZoom = value; }
        }

        /// <summary>
        /// Property to get and set the min orbit zoom distance.
        /// </summary>
        public float OrbitMinZoom
        {
            get { return camera.OrbitMinZoom; }
            set { camera.OrbitMinZoom = value; }
        }

        /// <summary>
        /// Property to get and set the distance from the target when orbiting.
        /// </summary>
        public float OrbitOffsetDistance
        {
            get { return camera.OrbitOffsetDistance; }
            set { camera.OrbitOffsetDistance = value; }
        }

        /// <summary>
        /// Property to get and set the camera orbit target position.
        /// </summary>
        public Vector3 OrbitTarget
        {
            get { return camera.OrbitTarget; }
            set { camera.OrbitTarget = value; }
        }

        /// <summary>
        /// Property to get and set the camera orientation.
        /// </summary>
        public Quaternion Orientation
        {
            get { return camera.Orientation; }
            set { camera.Orientation = value; }
        }

        /// <summary>
        /// Property to get and set the camera position.
        /// </summary>
        public Vector3 Position
        {
            get { return camera.Position; }
            set { camera.Position = value; }
        }


        /// <summary>
        /// Property to get the perspective projection matrix.
        /// </summary>
        public Matrix ProjectionMatrix
        {
            get { return camera.ProjectionMatrix; }
        }

        /// <summary>
        /// Property to get the perspective projection matrix.
        /// </summary>
        public Vector3 Direction
        {
            get { return camera.Direction; }
        }

        /// <summary>
        /// Property to get and set the mouse rotation speed.
        /// </summary>
        public float RotationSpeed
        {
            get { return rotationSpeed; }
            set { rotationSpeed = value; }
        }


        /// <summary>
        /// Property to get the viewing direction vector.
        /// </summary>
        public Vector3 ViewDirection
        {
            get { return camera.ViewDirection; }
        }

        /// <summary>
        /// Property to get the view matrix.
        /// </summary>
        public Matrix ViewMatrix
        {
            get { return camera.ViewMatrix; }
        }

        /// <summary>
        /// Property to get the concatenated view-projection matrix.
        /// </summary>
        public Matrix ViewProjectionMatrix
        {
            get { return camera.ViewProjectionMatrix; }
        }

        /// <summary>
        /// Property to get the camera's local X axis.
        /// </summary>
        public Vector3 XAxis
        {
            get { return camera.XAxis; }
        }

        /// <summary>
        /// Property to get the camera's local Y axis.
        /// </summary>
        public Vector3 YAxis
        {
            get { return camera.YAxis; }
        }

        /// <summary>
        /// Property to get the camera's local Z axis.
        /// </summary>
        public Vector3 ZAxis
        {
            get { return camera.ZAxis; }
        }

        public Vector3 Target
        {
            get { return camera.Target; }
        }

        #endregion
    }
}